package com.jingge.autojob.skeleton.model.register;

import com.jingge.autojob.skeleton.cluster.balancing.LoadBalancingStrategy;
import com.jingge.autojob.skeleton.cluster.balancing.LoadBalancingStrategyContext;
import com.jingge.autojob.skeleton.cluster.model.ClusterNode;
import com.jingge.autojob.skeleton.enumerate.LoadBalancingEnum;
import com.jingge.autojob.skeleton.framework.boot.AutoJobApplication;
import com.jingge.autojob.skeleton.framework.config.AutoJobConfigHolder;
import com.jingge.autojob.skeleton.framework.config.AutoJobConstant;
import com.jingge.autojob.skeleton.framework.network.client.CachedProxyClient;
import com.jingge.autojob.skeleton.framework.task.AutoJobTask;
import com.jingge.autojob.skeleton.lang.AutoJobException;
import com.jingge.autojob.skeleton.lifecycle.TaskEventFactory;
import com.jingge.autojob.skeleton.lifecycle.event.imp.TaskMissFireEvent;
import com.jingge.autojob.skeleton.lifecycle.manager.TaskEventManager;
import com.jingge.autojob.util.convert.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.sqlite.core.DB;

import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

/**
 * 远程注册器
 *
 * @author JingGe(* ^ ▽ ^ *)
 * @date 2023-09-19 13:45
 * @email 1158055613@qq.com
 */
@Slf4j
public class AutoJobRemoteRegister extends CachedProxyClient<IAutoJobRegister> implements IAutoJobRegister {
    private volatile IAutoJobRegister proxyRegister;
    private final LoadBalancingStrategy errorLoadBalancingTierDownStrategy = LoadBalancingEnum.DOWNGRADING.getLoadBalancingStrategy();

    public AutoJobRemoteRegister(IAutoJobRegister proxyRegister) {
        super(10, IAutoJobRegister.class);
        this.proxyRegister = proxyRegister;
    }

    public AutoJobRemoteRegister(String host, int port) {
        super(10, IAutoJobRegister.class);
        proxyRegister = getInstance(host, port);
    }

    public AutoJobRemoteRegister(ClusterNode clusterNode) {
        this(clusterNode.getHost(), clusterNode.getPort());
    }

    public synchronized AutoJobRemoteRegister change(String host, int port) {
        if (StringUtils.isEmpty(host) || port <= 0) {
            return this;
        }
        proxyRegister = getInstance(host, port, 2, 2, TimeUnit.SECONDS);
        return this;
    }

    public AutoJobRemoteRegister change(ClusterNode clusterNode) {
        if (clusterNode == null) {
            throw new AutoJobException("负载均衡失败，要转移到的节点为null");
        }
        return change(clusterNode.getHost(), clusterNode.getPort());
    }

    public AutoJobRemoteRegister loadBalancing(LoadBalancingStrategyContext loadBalancingStrategyContext, AutoJobTask task) {
        List<ClusterNode> solves = AutoJobApplication
                .getInstance()
                .getClusterManager()
                .getClusterContext()
                .getSolves();
        if (solves.size() == 0) {
            log.warn("本轮调度没有找到相关的从节点用于任务执行");
            if (task
                    .getTrigger()
                    .isNearTriggeringTime(task.getType() == AutoJobTask.TaskType.DB_TASK ? AutoJobConstant.dbSchedulerRate : AutoJobConstant.memorySchedulerRate)) {
                log.warn("任务{} miss fire", task.getId());
                TaskEventManager
                        .getInstance()
                        .publishTaskEvent(TaskEventFactory.newTaskMissFireEvent(task), TaskMissFireEvent.class, true);
            }
            return this;
        }
        ClusterNode chosen = loadBalancingStrategyContext.choose(solves, task);
        if (AutoJobConfigHolder
                .getInstance()
                .isDebugEnable() && chosen != null) {
            log.warn("任务：{}已根据负载均衡策略{}转移到执行节点{}执行", task.getId(), task
                    .getLoadBalancing()
                    .getName(), chosen.toString());
        }
        change(chosen);
        return this;
    }


    @Override
    public boolean registerTask(AutoJobTask task) {
        if (task == null) {
            return false;
        }
        if (task.getLoadBalancing() != null) {
            if (AutoJobConfigHolder
                    .getInstance()
                    .isDebugEnable()) {
                log.warn("本次任务：{}的调度将采用{}负载均衡策略", task.getId(), task
                        .getLoadBalancing()
                        .getName()
                        .toUpperCase());
            }
            loadBalancing(new LoadBalancingStrategyContext(task
                    .getLoadBalancing()
                    .getLoadBalancingStrategy()), task);
        }
        try {
            return proxyRegister.registerTask(task);
        } catch (Exception e) {
            return downgrading(task);
        }
    }

    @Override
    public boolean registerTask(AutoJobTask task, boolean isForced, long waitTime, TimeUnit unit) {
        if (task == null) {
            return false;
        }
        if (task.getLoadBalancing() != null) {
            loadBalancing(new LoadBalancingStrategyContext(task
                    .getLoadBalancing()
                    .getLoadBalancingStrategy()), task);
        }
        try {
            return proxyRegister.registerTask(task, isForced, waitTime, unit);
        } catch (Exception e) {
            return downgrading(task);
        }
    }

    private boolean downgrading(AutoJobTask task) {
        try {
            log.warn("任务{}按照原配置的负载均衡策略{}转移异常，将进行降级策略处理", task.getId(), task
                    .getLoadBalancing()
                    .getName());
            loadBalancing(new LoadBalancingStrategyContext(errorLoadBalancingTierDownStrategy), task);
            return proxyRegister.registerTask(task);
        } catch (Exception ignored) {
            return false;
        }
    }

    @Override
    public AutoJobTask takeTask() {
        throw new UnsupportedOperationException();
    }

    @Override
    public AutoJobTask takeTask(long waitTime, TimeUnit unit) {
        throw new UnsupportedOperationException();
    }

    @Override
    public AutoJobTask readTask() {
        throw new UnsupportedOperationException();
    }

    @Override
    public List<AutoJobTask> filter(Predicate<AutoJobTask> predicate) {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean removeTask(long taskId) {
        throw new UnsupportedOperationException();
    }

    @Override
    public AutoJobTask mergeAndReplaceTaskAndGet(long taskId, AutoJobTask newInstance) {
        throw new UnsupportedOperationException();
    }

    @Override
    public List<AutoJobTask> removeAndGetTask(long taskId) {
        throw new UnsupportedOperationException();
    }

    @Override
    public AutoJobTask removeAndGetTaskByScheduleQueueID(long scheduleQueueID) {
        throw new UnsupportedOperationException();
    }

    @Override
    public List<AutoJobTask> getTaskById(long taskId) {
        throw new UnsupportedOperationException();
    }

    @Override
    public AutoJobTask getTaskByScheduleQueueID(long scheduleQueueID) {
        throw new UnsupportedOperationException();
    }

    @Override
    public IAutoJobRegister setFilter(AbstractRegisterFilter filter) {
        throw new UnsupportedOperationException();
    }

    @Override
    public IAutoJobRegister setHandler(AbstractRegisterHandler handler) {
        throw new UnsupportedOperationException();
    }

    @Override
    public Iterator<AutoJobTask> iterator() {
        throw new UnsupportedOperationException();
    }

    @Override
    public int size() {
        throw new UnsupportedOperationException();
    }

}
